package handler

import (
	"context"
	"crypto/sha512"
	"fmt"
	"gitee.com/DonHz/electronic-business-systems/mxshop_srvs/user_srv/global"
	"gitee.com/DonHz/electronic-business-systems/mxshop_srvs/user_srv/model"
	"gitee.com/DonHz/electronic-business-systems/mxshop_srvs/user_srv/proto"
	"github.com/anaskhan96/go-password-encoder"
	"github.com/bwmarrin/snowflake"
	"github.com/golang/protobuf/ptypes/empty"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"gorm.io/gorm"
	"strings"
)

var options = &password.Options{16, 100, 32, sha512.New}

type UserServer struct {
	proto.UnimplementedUserServer
}

// 获取用户列表
func (s *UserServer) GetUserList(ctx context.Context, req *proto.PageInfo) (*proto.UserListResp, error) {
	fmt.Println("用户列表")
	var users []model.User
	res := global.DB.Find(&users)
	if res.Error != nil {
		return nil, res.Error
	}
	resp := &proto.UserListResp{}
	resp.Total = int32(res.RowsAffected)

	global.DB.Scopes(Paginate(int(req.Pn), int(req.PSize))).Find(&users)
	for _, user := range users {
		userInfoRsp := Model2Resp(user)
		resp.Data = append(resp.Data, &userInfoRsp)
	}
	return resp, nil
}

func (s *UserServer) GetUserByPhone(ctx context.Context, req *proto.PhoneReq) (*proto.UserInfoResp, error) {
	var user model.User
	res := global.DB.Where(&model.User{Phone: req.Phone}).First(&user)
	if res.Error != nil {
		return nil, res.Error
	}
	if res.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "用户不存在")
	}

	resp := Model2Resp(user)
	return &resp, nil
}

func (s *UserServer) GetUserByUserId(ctx context.Context, req *proto.UserIdReq) (*proto.UserInfoResp, error) {
	var user model.User
	res := global.DB.Where(&model.User{UserId: req.UserId}).First(&user)
	if res.Error != nil {
		return nil, res.Error
	}
	if res.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "用户不存在")
	}

	resp := Model2Resp(user)
	return &resp, nil
}

func (s *UserServer) CreateUser(ctx context.Context, req *proto.CreateUserInfo) (*proto.UserInfoResp, error) {
	var user model.User
	res := global.DB.Where(&model.User{Phone: req.Phone}).First(&user)
	if res.RowsAffected == 1 {
		return nil, status.Errorf(codes.AlreadyExists, "用户已存在")
	}
	user.Phone = req.Phone
	user.NickName = req.NickName
	// md5盐值加密，密码
	//options := &password.Options{16, 100, 32, sha512.New}
	salt, encodePwd := password.Encode(req.Password, options)
	user.Password = fmt.Sprintf("pbkdf2-sha512$%s$%s", salt, encodePwd)
	// 雪花算法生成 UserId
	node, err := snowflake.NewNode(1)
	if err != nil {
		return nil, status.Errorf(codes.Internal, err.Error())
	}
	userId := node.Generate()
	user.UserId = userId.Int64()
	// 保存到数据库
	res = global.DB.Create(&user)
	if res.Error != nil {
		return nil, status.Errorf(codes.Internal, res.Error.Error())
	}
	resp := Model2Resp(user)
	return &resp, nil
}

func (s *UserServer) UpdateUser(ctx context.Context, req *proto.UpdateUserInfo) (*emptypb.Empty, error) {
	var user model.User
	res := global.DB.First(&user, req.UserId)
	if res.Error != nil {
		return nil, res.Error
	}
	if res.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "用户不存在")
	}
	user.NickName = req.NickName
	user.Gender = req.Gender
	user.Birthday = req.Birthday

	res = global.DB.Save(user)
	if res.Error != nil {
		return nil, status.Errorf(codes.Internal, res.Error.Error())
	}
	return &empty.Empty{}, nil
}

func (s *UserServer) CheckPassword(ctx context.Context, req *proto.CheckPwdInfo) (*proto.CheckResp, error) {
	passwordInfo := strings.Split(req.EncryptedPassword, "$")
	//options := &password.Options{16, 100, 32, sha512.New}
	check := password.Verify(req.Password, passwordInfo[1], passwordInfo[2], options)
	return &proto.CheckResp{Success: check}, nil
}

// 分页
func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		if page <= 0 {
			page = 1
		}
		switch {
		case pageSize > 50:
			pageSize = 50
		case pageSize <= 0:
			pageSize = 10
		}
		offset := (page - 1) * pageSize
		return db.Offset(offset).Limit(pageSize)
	}
}

func Model2Resp(user model.User) proto.UserInfoResp {
	userInfoResp := proto.UserInfoResp{
		Id:       user.Id,
		UserId:   user.UserId,
		Password: user.Password,
		NickName: user.NickName,
		Phone:    user.Phone,
		Birthday: user.Birthday,
		Gender:   user.Gender,
		Role:     user.Role,
	}
	return userInfoResp
}
